package edu.spsu.cs4253;

import java.lang.*;
import java.util.*;
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.net.*;

public class Peer2Peer implements Runnable {

	//States of Connections
   public final static int NULL = 0;
   public final static int DISCONNECTED = 1;
   public final static int DISCONNECTING = 2;
   public final static int BEGIN_CONNECT = 3;
   public final static int CONNECTED = 4;
   public final static String statusMessages[] = {
      " Error! Could not connect!", " Disconnected",
      " Disconnecting...", " Connecting...", " Connected"
   };
   public final static Peer2Peer tcpObj = new Peer2Peer();
   public final static String END_CHAT_SESSION = new Character((char)0).toString(); 
   public static int connectionStatus = DISCONNECTED;
   public static String statusString = statusMessages[connectionStatus];
   
   //IPs, Port, sending stuff
   public static String serverIP = "localhost";
   public static int port;
   public static boolean isHost = true;
   public static StringBuffer toAppend = new StringBuffer("");
   public static StringBuffer toSend = new StringBuffer("");

   public static File file = new File ("/Users/kdebrito/Desktop/echo.mp3");
   //GUI
   public static JFrame mainFrame = null;
   public static JLabel fileText = null;
   public static JButton browseButton = null;
   public static JTextField fileBrowse = null;
   public static JTextField sendFile = null;
   public static JPanel statusBar = null;
   public static JLabel statusField = null;
   public static JTextField statusColor = null;
   public static JTextField ipField = null;
   public static JTextField portField = null;
   public static JRadioButton guestOption = null;
   public static JButton connectButton = null;
   public static JButton disconnectButton = null;
   public static JButton sendButton = null;
   public static JButton retrieveButton = null;

   //Sockets, Inputs, Outputs
   public static Socket socket = null;
   public static BufferedReader in = null;
   public static PrintWriter out = null;


   //Left side of GUI
   private static JPanel initOptionsPane() {
      JPanel pane = null;
      ActionAdapter buttonListener = null;
      ActionAdapter send = null;
      ActionAdapter retrieve = null;
     
      JPanel optionsPane = new JPanel(new GridLayout(4, 1));

      //Server IP Input Field
      pane = new JPanel(new FlowLayout(FlowLayout.RIGHT));
      pane.add(new JLabel("Server IP:"));
      ipField = new JTextField(10); 
      ipField.setText(serverIP);
      ipField.setEnabled(false);
      ipField.addFocusListener(new FocusAdapter() {
            public void focusLost(FocusEvent e) {
               ipField.selectAll();
            
               if (connectionStatus != DISCONNECTED) {
                  changeStatusGUI(NULL, true);
               }
               else {
                  serverIP = ipField.getText();
               }
            }
         });
      pane.add(ipField);
      optionsPane.add(pane);

      //Port Number Input Field
      pane = new JPanel(new FlowLayout(FlowLayout.RIGHT));
      pane.add(new JLabel("Server Port:"));
      portField = new JTextField(10); 
      portField.setEditable(true);
      portField.setText((new Integer(port)).toString());
      portField.addFocusListener(new FocusAdapter() {
            public void focusLost(FocusEvent e) {
            
               if (connectionStatus != DISCONNECTED) {
                  changeStatusGUI(NULL, true);
               }
               else {
                  int temp;
                  try {
                     temp = Integer.parseInt(portField.getText());
                     port = temp;
                  }
                  catch (NumberFormatException nfe) {
                     portField.setText((new Integer(port)).toString());
                     mainFrame.repaint();
                  }
               }
            }
         });
      pane.add(portField);
      optionsPane.add(pane);

      
      buttonListener = new ActionAdapter() {
            public void actionPerformed(ActionEvent e) {
               if (connectionStatus != DISCONNECTED) {
                  changeStatusGUI(NULL, true);
               }
               else {
                  isHost = e.getActionCommand().equals("host");

                 
                  if (isHost) {
                     ipField.setEnabled(false);
                     ipField.setText("localhost");
                     serverIP = "localhost";
                  }
                  else {
                     ipField.setEnabled(true);
                  }
               }
            }
         };
      //Radio Buttons to choose between Server or Client
      ButtonGroup bg = new ButtonGroup();
      guestOption = new JRadioButton("Client", false);
      guestOption.setMnemonic(KeyEvent.VK_G);
      guestOption.setActionCommand("client");
      guestOption.addActionListener(buttonListener);
 
      bg.add(guestOption);
      pane = new JPanel(new GridLayout(1, 2));
     
      pane.add(guestOption);
      optionsPane.add(pane);

      //Buttons for Connecting and Disconnecting 
      JPanel buttonPane = new JPanel(new GridLayout(1, 2));
      buttonListener = new ActionAdapter() {
            public void actionPerformed(ActionEvent e) {
               // Starts to Connect to Server
               if (e.getActionCommand().equals("connect")) {
                  changeStatusGUI(BEGIN_CONNECT, true);
               }
               
               else {
                  changeStatusGUI(DISCONNECTING, true);
               }
               
              
            }
         };
         
         send = new ActionAdapter() {
        	 public void actionPerformed(ActionEvent e) {
        		 if (e.getActionCommand().equals("send")) {
        			 try {
						sendtoServer(file);
					} catch (Exception e1) {
						// TODO Auto-generated catch block
						e1.printStackTrace();
					}
        			 
        		 }
        	 }
         };
         
         retrieve = new ActionAdapter() {
        	 public void actionPerformed(ActionEvent e) {
        		 if (e.getActionCommand().equals("retrieve")) {
        			 try {
						sendtoServer(file);
					} catch (Exception e1) {
						// TODO Auto-generated catch block
						e1.printStackTrace();
					}
        			 
        		 }
        	 }
         };
      connectButton = new JButton("Connect");
      connectButton.setMnemonic(KeyEvent.VK_C);
      connectButton.setActionCommand("connect");
      connectButton.addActionListener(buttonListener);
      connectButton.setEnabled(true);
      
      disconnectButton = new JButton("Disconnect");
      disconnectButton.setMnemonic(KeyEvent.VK_D);
      disconnectButton.setActionCommand("disconnect");
      disconnectButton.addActionListener(buttonListener);
      disconnectButton.setEnabled(false);
      
      sendButton = new JButton("Send File");
      sendButton.setActionCommand("send");
      sendButton.addActionListener(send);
      sendButton.setEnabled(false);
      
      retrieveButton = new JButton("Retreive File");
      retrieveButton.setActionCommand("retrieve");
      retrieveButton.addActionListener(retrieve);
      retrieveButton.setEnabled(false);
      
      buttonPane.add(connectButton);
      buttonPane.add(disconnectButton);
      buttonPane.add(sendButton);
      buttonPane.add(retrieveButton);
      optionsPane.add(buttonPane);

      return optionsPane;
   }
   
   //Starts building the GUI 
   private static void initGUI() {
     
      statusField = new JLabel();
      statusField.setText(statusMessages[DISCONNECTED]);
      statusColor = new JTextField(1);
      statusColor.setBackground(Color.red);
      statusColor.setEditable(false);
      statusBar = new JPanel(new BorderLayout());
      statusBar.add(statusColor, BorderLayout.WEST);
      statusBar.add(statusField, BorderLayout.CENTER);

     
      JPanel optionsPane = initOptionsPane();

      JPanel mainPane = new JPanel(new BorderLayout());
      mainPane.add(statusBar, BorderLayout.SOUTH);
      mainPane.add(optionsPane, BorderLayout.WEST);
      

      mainFrame = new JFrame("Peer 2 Peer Client");
      mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      mainFrame.setContentPane(mainPane);
      mainFrame.setSize(mainFrame.getPreferredSize());
      mainFrame.setLocation(200, 200);
      mainFrame.pack();
      mainFrame.setVisible(true);
   }

   //Checks Status of Connection...Threaded
   private static void changeStatus(int newConnectStatus, boolean noError) {

      if (newConnectStatus != NULL) {
         connectionStatus = newConnectStatus;
      }


      if (noError) {
         statusString = statusMessages[connectionStatus];
      }

      else {
         statusString = statusMessages[NULL];
      }

      SwingUtilities.invokeLater(tcpObj);
   }

   //Checks Status of Connection...Non-Threaded
   private static void changeStatusGUI(int newConnectStatus, boolean noError) {

      if (newConnectStatus != NULL) {
         connectionStatus = newConnectStatus;
      }
      if (noError) {
         statusString = statusMessages[connectionStatus];
      }
      
      else {
         statusString = statusMessages[NULL];
      }


      tcpObj.run();
   }

   //Displays on chat box
   private static void appendToChatBox(String s) {
      synchronized (toAppend) {
         toAppend.append(s);
      }
   }

   //Sends to the other peer
   private static void sendString(String s) {
      synchronized (toSend) {
         toSend.append(s + "\n");
      }
   }

  //Closes the Sockets
   private static void closeSockets() {
     
      try {
         if (socket != null) {
            socket.close();
            socket = null;
         }
      }
      catch (IOException e) { socket = null; }

      try {
         if (in != null) {
            in.close();
            in = null;
         }
      }
      catch (IOException e) { in = null; }

      if (out != null) {
         out.close();
         out = null;
      }
   }


   //Controls the animations in the GUI
   public void run() {
      switch (connectionStatus) {
      case DISCONNECTED:
         connectButton.setEnabled(true);
         disconnectButton.setEnabled(false);
         ipField.setEnabled(true);
         portField.setEnabled(true);
         retrieveButton.setEnabled(false);
         guestOption.setEnabled(true);
         statusColor.setBackground(Color.red);
         break;

      case DISCONNECTING:
         connectButton.setEnabled(false);
         disconnectButton.setEnabled(false);
         sendButton.setEnabled(false);
         ipField.setEnabled(false);
         portField.setEnabled(false);
         retrieveButton.setEnabled(false);
         guestOption.setEnabled(false);
         statusColor.setBackground(Color.orange);
         break;

      case CONNECTED:
         connectButton.setEnabled(false);
         disconnectButton.setEnabled(true);
         sendButton.setEnabled(true);
         retrieveButton.setEnabled(true);
         
         ipField.setEnabled(false);
         portField.setEnabled(false);
        
         guestOption.setEnabled(false);
        
         statusColor.setBackground(Color.green);
         break;

      case BEGIN_CONNECT:
         connectButton.setEnabled(false);
         disconnectButton.setEnabled(false);
         sendButton.setEnabled(false);
         retrieveButton.setEnabled(false);
         ipField.setEnabled(false);
         portField.setEnabled(false);
        
         guestOption.setEnabled(false);
       
         statusColor.setBackground(Color.orange);
         break;
      }

    
      ipField.setText(serverIP);
      portField.setText((new Integer(port)).toString());
      
      guestOption.setSelected(!isHost);
      statusField.setText(statusString);
      toAppend.setLength(0);

      mainFrame.repaint();
   }
   
  public static void sendtoServer (File file) throws Exception {
	  OutputStream out = socket.getOutputStream();
	  FileInputStream in = new FileInputStream(file);
	  byte[] buffer = new byte[1024*1024];
	  int bytesRead = 0;
	  
	  while((bytesRead = in.read(buffer)) > 0) {
		  out.write(buffer, 0, bytesRead);
	  }
	  System.out.println("BYTES SENT...... ");
	  in.close();
  }
  
  public static void receiveFile (String fileName) throws Exception {
	File file = null;
	int fileSize = 6022386;
	byte[] buffer = new byte [fileSize];
	
	file = new File ("/Users/kdebrito/Desktop/" + fileName);
	InputStream in = socket.getInputStream();
	FileOutputStream out = new FileOutputStream(file);
	BufferedOutputStream bufferedOut = new BufferedOutputStream(out);
	
	int bytesRead = in.read(buffer, 0, buffer.length);
	int current = bytesRead;
	
	do {
		bytesRead = in.read(buffer, current, (buffer.length-current));
		if (bytesRead >= 0)
			current += bytesRead;
	}
	
	while(bytesRead>-1);
	
	System.out.println("BYTES RECEIVED FROM SERVER: " + bytesRead);
	bufferedOut.flush();
	bufferedOut.close();
	
  }
  
   //This is all the logic for sending, receiving, and creating the sockets. 
   public static void main(String args[]) {
      String s;

      initGUI();

      while (true) {
         try { 
            Thread.sleep(10);
         }
         catch (InterruptedException e) {}

         switch (connectionStatus) {
         case BEGIN_CONNECT:
            try {
               
            	
            	
                  socket = new Socket(serverIP, port);
                  
               
               
               //Gets input from user
               in = new BufferedReader(new 
                  InputStreamReader(socket.getInputStream()));
               
               //Gets output 
               out = new PrintWriter(socket.getOutputStream(), true);
               changeStatus(CONNECTED, true);
            }
        
            //Closes sockets if any problems 
            catch (IOException e) {
               closeSockets();
               changeStatus(DISCONNECTED, false);
            }
            break;

         case CONNECTED:
        	 try {
             	
                 // Send data
                 if (toSend.length() != 0) {
                    out.print(toSend); out.flush();
                    toSend.setLength(0);
                    changeStatus(NULL, true);
                 }
                 
                 
                 // Receives data
                 if (in.ready()) {
                    s = in.readLine();
                    if ((s != null) &&  (s.length() != 0)) {
                     
                       if (s.equals(END_CHAT_SESSION)) {
                          changeStatus(DISCONNECTING, true);
                       }

                       
                       else {
                          appendToChatBox(s + "\n");
                          changeStatus(NULL, true);
                       }
                    }
                 }
              }
              catch (IOException e) {
                 closeSockets();
                 changeStatus(DISCONNECTED, false);
              }
              break;

         case DISCONNECTING:
           
            out.print(END_CHAT_SESSION); 
            out.flush();

            
            closeSockets();
            changeStatus(DISCONNECTED, true);
            break;

         default: break; 
         }
      }
   }
}

//Controls actions
class ActionAdapter implements ActionListener {
   public void actionPerformed(ActionEvent e) {}
}

